package com.simple.parser;

import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.simple .config.SyncConfig;
import com.simple .config.SyncTaskConfig;
import com.simple .parser.model.AdvanceUpdateContext;
import com.simple.parser.model.Tuple;

import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目名称：foursmile-local-etl
 * 类名称：ContrastParserTemplate
 * 类描述：比对处理器模板
 * 创建时间：2022/10/20
 *
 * @author jiangjunjie   (E-mail:jiangjunjie@foursmile001.com)
 * @version v1.0
 */
public abstract class ContrastParserTemplate {

    public void parse(SyncConfig syncConfig) throws SQLException {
        this.prep(syncConfig);

        String sourceEnv = syncConfig.getSourceEnv();
        String targetEnv = syncConfig.getTargetEnv();

        Db sourceDb = Db.use(sourceEnv);
        Db targetDb = Db.use(targetEnv);

        List<SyncTaskConfig> syncTaskConfigs = syncConfig.getTasks();
        for (SyncTaskConfig syncTaskConfig : syncTaskConfigs) {
            this.taskPrep(syncTaskConfig);


            SyncTaskConfig.ContrastStrategy contrastStrategy = syncTaskConfig.getContrastStrategy();

            List<Entity> sourceEntityList = sourceDb.query(syncTaskConfig.getSelectSql());
            Map<Tuple, Entity> sourceEntityMap = this.toMap(sourceEntityList, contrastStrategy);

            List<Entity> targetEntityList = targetDb.query(syncTaskConfig.getTargetSelectSql());
            Map<Tuple, Entity> targetEntityMap = this.toMap(targetEntityList, contrastStrategy);

            if (sourceEntityList.isEmpty() && targetEntityList.isEmpty()) {
                continue;
            }

            List<String> updateColumns = contrastStrategy.getUpdateColumns();
            {//智能获取【需更新字段】
                Entity entity;
                if (!sourceEntityList.isEmpty()) {
                    entity = sourceEntityList.get(0);
                } else {
                    entity = targetEntityList.get(0);
                }
                if (updateColumns.isEmpty()) {
                    for (String str : entity.keySet()) {
                        if (!contrastStrategy.getPrimaryKey().contains(str)) {
                            updateColumns.add(str);
                        }
                    }
                }
            }


            List<Entity> deleteList = this.contrastDelete(sourceEntityMap, targetEntityMap);
            this.handleDeleteList(deleteList, syncTaskConfig);

            List<Entity> insertList = this.contrastInsert(sourceEntityMap, targetEntityMap);
            this.handleInsertList(insertList, syncTaskConfig);

            if (contrastStrategy.getEnableUpdate() && !updateColumns.isEmpty()) {
                List<AdvanceUpdateContext> updateContextList = this.contrastUpdate(sourceEntityMap, targetEntityMap, updateColumns);
                this.handleUpdateList(updateContextList, syncTaskConfig);
            }
        }

    }

    private Map<Tuple, Entity> toMap(List<Entity> entityList, SyncTaskConfig.ContrastStrategy contrastStrategy) {
        Map<Tuple, Entity> entityMap = new HashMap<>(entityList.size());
        for (Entity targetEntity : entityList) {
            Object[] objects = contrastStrategy.getPrimaryKey().stream().map(targetEntity::get).toArray();
            Tuple tuple = new Tuple(objects);
            entityMap.put(tuple, targetEntity);
        }
        return entityMap;
    }

    private List<Entity> contrastDelete(Map<Tuple, Entity> sourceEntityMap, Map<Tuple, Entity> targetEntityMap) {
        List<Entity> deleteList = targetEntityMap.entrySet().stream().map(entry -> {
            Entity entity = sourceEntityMap.get(entry.getKey());
            if (entity == null) {
                return entry.getValue();
            } else {
                return null;
            }
        }).filter(Objects::nonNull).collect(Collectors.toList());
        return deleteList;
    }


    private List<Entity> contrastInsert(Map<Tuple, Entity> sourceEntityMap, Map<Tuple, Entity> targetEntityMap) {
        List<Entity> insertList = sourceEntityMap.entrySet().stream().map(entry -> {
            Entity entity = targetEntityMap.get(entry.getKey());
            if (entity == null) {
                return entry.getValue();
            } else {
                return null;
            }
        }).filter(Objects::nonNull).collect(Collectors.toList());
        return insertList;
    }


    private List<AdvanceUpdateContext> contrastUpdate(Map<Tuple, Entity> sourceEntityMap, Map<Tuple, Entity> targetEntityMap, List<String> updateColumns) {
        List<AdvanceUpdateContext> updateContextList = targetEntityMap.entrySet().stream().map(entry -> {
            Entity oldData = sourceEntityMap.get(entry.getKey());
            if (oldData == null) {
                return null;
            } else {
                Entity newData = entry.getValue();
                List<AdvanceUpdateContext.Item> items = new ArrayList<>();

                for (String updateColumn : updateColumns) {
                    Object oldObj = oldData.get(updateColumn);
                    Object newObj = newData.get(updateColumn);
                    if (!Objects.equals(oldObj, newObj)) {
                        AdvanceUpdateContext.Item item = new AdvanceUpdateContext.Item();
                        item.setColumn(updateColumn);
                        item.setOldData(oldObj);
                        item.setNewData(newObj);
                        items.add(item);
                    }
                }
                if (items.isEmpty()) {
                    return null;
                }
                AdvanceUpdateContext advanceUpdateContext = new AdvanceUpdateContext();
                advanceUpdateContext.setNewData(newData);
                advanceUpdateContext.setItems(items);
                return advanceUpdateContext;
            }
        }).filter(Objects::nonNull).collect(Collectors.toList());
        return updateContextList;
    }

    protected void prep(SyncConfig syncConfig) {
    }


    protected void taskPrep(SyncTaskConfig syncTaskConfig) {
    }


    protected abstract void handleInsertList(List<Entity> insertList, SyncTaskConfig syncTaskConfig);

    protected abstract void handleDeleteList(List<Entity> deleteList, SyncTaskConfig syncTaskConfig);

    protected abstract void handleUpdateList(List<AdvanceUpdateContext> updateContextList, SyncTaskConfig syncTaskConfig);

}
